package roborally.model;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;

/**
 * An enumeration introducing different units used to
 * express amounts of energy.
 *   In its current form, the class only supports the watt-second,
 *   the joule and the kilojoule.
 * 
 * @version  1.0
 * @author	Pieter Robberechts	(1e Bach Informatica)
 * @author	Jeroen Sanders 		(1e Bach Informatica)
 */
public enum EnergyUnit {
	
	Ws, J, kJ;
	
	/**
	 * Return the value of 1 unit of this energy unit in the other
	 * energy unit.
	 * 
	 * @param   other
	 *          The unit to convert to.
	 * @return  The resulting conversion factor is effective.
	 *        | result != null
	 * @return  The resulting conversion factor is positive.
	 *        | result.signum() == 1
	 * @return  If this energy unit is the same as the other
	 *          energy unit, BigDecimal.ONE is returned.
	 *        | if (this == other)
	 *        |   then (result == BigDecimal.ONE)
	 * @return  If this energy unit is not the same as the other
	 *          energy unit, the resulting conversion factor has the
	 *          precision as established by the energy unit context.
	 *        | if (this != other)
	 *        |   then (result.precision() ==
	 *        |             energyUnitContext.getPrecision())
	 * @return  The resulting conversion factor is the inverse of
	 *          the conversion factor from the other energy unit to this
	 *          energy unit.
	 *        |  result.equals
	 *        |     (BigDecimal.ONE.divide
	 *        |       (other.toUnit(this),energyUnitContext))
	 * @throws  IllegalArgumentException
	 *          The given energy unit is not effective.
	 *        | (other == null)
	 */
	public BigDecimal toUnit(EnergyUnit other)
			throws IllegalArgumentException
			{
		if (other == null)
			throw new IllegalArgumentException
			("Non effective energy unit!");
		if (conversionFactor[this.ordinal()][other.ordinal()] == null)
			conversionFactor[this.ordinal()][other.ordinal()] =
			BigDecimal.ONE.divide(
					conversionFactor[other.ordinal()][this.ordinal()],
					energyUnitContext);
		return conversionFactor[this.ordinal()][other.ordinal()];
			}

	/**
	 * Variable referencing a two-dimensional array registering
	 * conversion factors between energy units. The first level is
	 * indexed by the ordinal number of the unit to convert
	 * from; the ordinal number to convert to is used to index
	 * the second level.
	 */
	private static BigDecimal[][] conversionFactor =
			new BigDecimal[3][3];

	static {
		// Initialization of the upper part of the conversion table.
		// Other factors are computed and registered the first time
		// they are queried.
		conversionFactor[Ws.ordinal()][Ws.ordinal()] =
				BigDecimal.ONE;
		conversionFactor[Ws.ordinal()][J.ordinal()] =
				BigDecimal.ONE;
		conversionFactor[Ws.ordinal()][kJ.ordinal()] =
				new BigDecimal(BigInteger.valueOf(1),3);
		conversionFactor[J.ordinal()][J.ordinal()] =
				BigDecimal.ONE;
		conversionFactor[J.ordinal()][kJ.ordinal()] =
				new BigDecimal(BigInteger.valueOf(1),3);
		conversionFactor[kJ.ordinal()][kJ.ordinal()] =
				BigDecimal.ONE;
	}

	/**
	 * Variable referencing the mathematical context used in
	 * energy units arithmetic.
	 *  @return   The energy unit context has a precision of 6
	 *            digits.
	 *          | energyUnitContext.getPrecision() == 6
	 *  @return   The energy unit context uses the rounding mode
	 *            HALF_DOWN.
	 *           | energyUnitContext.getRoundingMode() ==
	 *           |   RoundingMode.HALF_DOWN
	 */
	public static final MathContext energyUnitContext =
			new MathContext(6,RoundingMode.HALF_DOWN);

}
